index.js ➔ floatToInt_   A
last analyzed

Complexity

Conditions 2

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
/*
2
 * Copyright (c) 2017-2018 Rafael da Silva Rocha.
3
 *
4
 * Permission is hereby granted, free of charge, to any person obtaining
5
 * a copy of this software and associated documentation files (the
6
 * "Software"), to deal in the Software without restriction, including
7
 * without limitation the rights to use, copy, modify, merge, publish,
8
 * distribute, sublicense, and/or sell copies of the Software, and to
9
 * permit persons to whom the Software is furnished to do so, subject to
10
 * the following conditions:
11
 *
12
 * The above copyright notice and this permission notice shall be
13
 * included in all copies or substantial portions of the Software.
14
 *
15
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
 *
23
 */
24
25
/**
26
 * @fileoverview A module to change the bit depth of PCM samples.
27
 * @see https://github.com/rochars/bitdepth
28
 */
29
30
/** @module bitdepth */
31
32
/**
33
 * Change the bit depth of PCM samples.
34
 * @param {!Array|!TypedArray} samples The original samples.
35
 * @param {string} bithDepth The original bit depth.
36
 * @param {!TypedArray} newSamples The output array.
37
 * @param {string} targetBitDepth The target bit depth.
38
 * @throws {Error} If original or target bit depths are not valid.
39
 */
40
export function changeBitDepth(samples, bithDepth, newSamples, targetBitDepth) {
41
  // float to float, just copy the values
42
  if (["32f","64"].indexOf(bithDepth) > -1 &&
43
    ["32f","64"].indexOf(targetBitDepth) > -1) {
44
    newSamples.set(samples);
45
    return;
46
  }
47
  validateBitDepth_(bithDepth);
48
  validateBitDepth_(targetBitDepth);
49
  /** @type {!Function} */
50
  let toFunction = getBitDepthFunction_(bithDepth, targetBitDepth);
51
  /** @type {!Object<string, number>} */
52
  let options = {
53
    oldMin: Math.pow(2, parseInt(bithDepth, 10)) / 2,
54
    newMin: Math.pow(2, parseInt(targetBitDepth, 10)) / 2,
55
    oldMax: (Math.pow(2, parseInt(bithDepth, 10)) / 2) - 1,
56
    newMax: (Math.pow(2, parseInt(targetBitDepth, 10)) / 2) - 1,
57
  };
58
  // sign the samples if original is 8-bit
59
  sign8Bit_(bithDepth, samples, true);
60
  // FP to int need the original samples to be clipped
61
  // at 1 and -1 as files can have samples out of those boundaries
62
  if (["32f","64"].indexOf(bithDepth) > -1) {
63
    truncateSamples_(samples);
64
  }
65
  // change the resolution of the samples
66
  for (let i = 0; i< samples.length; i++) {        
67
    newSamples[i] = toFunction(samples[i], options);
68
  }
69
  // unsign the samples if target is 8-bit
70
  sign8Bit_(targetBitDepth, newSamples, false);
71
}
72
73
/**
74
 * Change the bit depth from int to int.
75
 * @param {number} sample The sample.
76
 * @param {!Object<string, number>} args Data about the bit depths.
77
 * @return {number}
78
 * @private
79
 */
80
function intToInt_(sample, args) {
81
  if (sample > 0) {
82
    sample = parseInt((sample / args.oldMax) * args.newMax, 10);
83
  } else {
84
    sample = parseInt((sample / args.oldMin) * args.newMin, 10);
85
  }
86
  return sample;
87
}
88
89
/**
90
 * Change the bit depth from float to int.
91
 * @param {number} sample The sample.
92
 * @param {!Object<string, number>} args Data about the bit depths.
93
 * @return {number}
94
 * @private
95
 */
96
function floatToInt_(sample, args) {
97
  return parseInt(
98
    sample > 0 ? sample * args.newMax : sample * args.newMin, 10);
99
}
100
101
/**
102
 * Change the bit depth from int to float.
103
 * @param {number} sample The sample.
104
 * @param {!Object<string, number>} args Data about the bit depths.
105
 * @return {number}
106
 * @private
107
 */
108
function intToFloat_(sample, args) {
109
  return sample > 0 ? sample / args.oldMax : sample / args.oldMin;
110
}
111
112
/**
113
 * Return the function to change the bit depth of a sample.
114
 * @param {string} original The original bit depth of the data.
115
 * @param {string} target The new bit depth of the data.
116
 * @return {!Function}
117
 * @private
118
 */
119
function getBitDepthFunction_(original, target) {
120
  /** @type {!Function} */
121
  let func = function(x) {return x;};
122
  if (original != target) {
123
    if (["32f", "64"].includes(original)) {
124
      func = floatToInt_;
125
    } else {
126
      if (["32f", "64"].includes(target)) {
127
        func = intToFloat_;
128
      } else {
129
        func = intToInt_;
130
      }
131
    }
132
  }
133
  return func;
134
}
135
136
/**
137
 * Validate the bit depth.
138
 * @param {string} bitDepth The original bit depth.
139
 * @throws {Error} If bit depth is not valid.
140
 * @private
141
 */
142
function validateBitDepth_(bitDepth) {
143
  if ((bitDepth != "32f" && bitDepth != "64") &&
144
      (parseInt(bitDepth, 10) < "8" || parseInt(bitDepth, 10) > "53")) {
145
    throw new Error("Invalid bit depth.");
146
  }
147
}
148
149
/**
150
 * Truncate float samples on overflow.
151
 * @param {!Array|!TypedArray} samples The samples.
152
 * @private
153
 */
154
function truncateSamples_(samples) {
155
  /** @type {number} */   
156
  let len = samples.length;
157
  for (let i=0; i<len; i++) {
158
    if (samples[i] > 1) {
159
      samples[i] = 1;
160
    } else if (samples[i] < -1) {
161
      samples[i] = -1;
162
    }
163
  }
164
}
165
166
/**
167
 * Sign samples if they are 8-bit.
168
 * @param {string} bitDepth The bit depth code.
169
 * @param {!Array|!TypedArray} samples The samples.
170
 * @param {boolean} sign True to sign, false to unsign.
171
 * @private
172
 */
173
function sign8Bit_(bitDepth, samples, sign) {
174
  if (bitDepth == "8") {
175
    let factor = sign ? -128 : 128;
176
    for (let i = 0; i < samples.length; i++) {
177
      samples[i] = samples[i] += factor;
178
    }
179
  }
180
}
181